package com.yzy.fly.config;

import com.yzy.fly.FlySpringMap;
import com.yzy.fly.util.FieldUtil;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.method.ControllerAdviceBean;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

public class FlyRequestMappingHandlerAdapter extends RequestMappingHandlerAdapter {


    @Override
    public void afterPropertiesSet() {
        // Do this first, it may add ResponseBody advice beans
        super.afterPropertiesSet();
//        if (this.argumentResolvers == null) {
//            List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
//            this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
//        }
//        if (this.initBinderArgumentResolvers == null) {
//            List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
//            this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
//        }
//        if (this.returnValueHandlers == null) {
//            List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
//            this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
//        }

        Field field_modelAttributeAdviceCache = FieldUtil.getField(RequestMappingHandlerAdapter.class, "modelAttributeAdviceCache", false);
        Field field_initBinderAdviceCache = FieldUtil.getField(RequestMappingHandlerAdapter.class, "initBinderAdviceCache", false);
        Field field_requestResponseBodyAdvice = FieldUtil.getField(RequestMappingHandlerAdapter.class, "requestResponseBodyAdvice", false);

        Map<ControllerAdviceBean, Set<Method>> modelAttributeAdviceCache = FieldUtil.getFieldValue(field_modelAttributeAdviceCache, this);
        Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = FieldUtil.getFieldValue(field_initBinderAdviceCache, this);
        List<Object> requestResponseBodyAdvice = FieldUtil.getFieldValue(field_requestResponseBodyAdvice, this);

        int modelSize = modelAttributeAdviceCache.size();
        int binderSize = initBinderAdviceCache.size();
        int reqCount = getBodyAdviceCount(requestResponseBodyAdvice, RequestBodyAdvice.class);
        int resCount = getBodyAdviceCount(requestResponseBodyAdvice, ResponseBodyAdvice.class);

//        if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0) {
//            logger.debug("ControllerAdvice beans: none");
//        } else {
//            logger.debug("ControllerAdvice beans: " + modelSize + " @ModelAttribute, " + binderSize +
//                    " @InitBinder, " + reqCount + " RequestBodyAdvice, " + resCount + " ResponseBodyAdvice");
//        }
        FlySpringMap.getInstance().add("RequestMappingHandlerAdapter.Count.@ModelAttribute", modelSize);
        FlySpringMap.getInstance().add("RequestMappingHandlerAdapter.Count.@InitBinder", binderSize);
        FlySpringMap.getInstance().add("RequestMappingHandlerAdapter.Count.RequestBodyAdvice", reqCount);
        FlySpringMap.getInstance().add("RequestMappingHandlerAdapter.Count.ResponseBodyAdvice", resCount);
    }

    private int getBodyAdviceCount(List<Object> advice, Class<?> adviceType) {
        return RequestBodyAdvice.class.isAssignableFrom(adviceType) ?
                getAdviceByType(advice, RequestBodyAdvice.class).size() :
                getAdviceByType(advice, ResponseBodyAdvice.class).size();
    }

    @SuppressWarnings("unchecked")
    public static <T> List<T> getAdviceByType(List<Object> requestResponseBodyAdvice, Class<T> adviceType) {
        if (requestResponseBodyAdvice != null) {
            List<T> result = new ArrayList<>();
            for (Object advice : requestResponseBodyAdvice) {
                Class<?> beanType = (advice instanceof ControllerAdviceBean ?
                        ((ControllerAdviceBean) advice).getBeanType() : advice.getClass());
                if (beanType != null && adviceType.isAssignableFrom(beanType)) {
                    result.add((T) advice);
                }
            }
            return result;
        }
        return Collections.emptyList();
    }


}
